﻿#include <QTransform>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QDataStream>

#include "CPlcScene.h"
//预定义的宏，能打印文件目录和程序行
//#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

CPlcScene::CPlcScene(QGraphicsScene *parent) : QGraphicsScene(parent)
{
    mInitialStatus    = false;
    mCommentIsVisible = false;
    mLastWidth        = 0;
    mPowerRail        = NULL;
    mStepIndex        = 0;
    mScale            = 0;
    updateCommentList();
}

CPlcScene::~CPlcScene()
{
    if (mPowerRail != NULL)
    {
        removeItem(mPowerRail);
    }
}

void CPlcScene::initScene()
{
    //初始化函数只执行一次
    if (mInitialStatus) return;

    QRectF rect = this->sceneRect();
    mMixHeight  = rect.height();

    // TODO 初始化PLC梯形图列表数据源
    if (!readItemList(mListView))
    {
        //当无PLC文件时，默认初始化一行空元件
        mListView.clear();
        rowList list;
        for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
        {
            //空元件
            SItemData itemData;
            itemData.pItem    = new CPlcItemSpace(rect, plc::Empty, "");
            itemData.pComment = NULL;
            memset(&itemData.component, 0, sizeof(itemData.component));
            itemData.component.type = plc::Empty;
            list.append(itemData);
            addItem(itemData.pItem);
        }
        mListView.append(list);
    }
    //生成电流母线
    mPowerRail = new CPlcItemBranch(rect, plc::BranchDown, "");
    this->addItem(mPowerRail);

    //临时给一个界面宽度，开始初始化梯形图元件
    setSceneWidth(10);

    //默认选中第一个元件
    mListView.at(0).at(0).pItem->setSelectActive(true);

    //初始化完成
    mInitialStatus = true;

    //记录回退步
    addStep();
}

//用于外部调整场景宽
void CPlcScene::updateSceneWidth(double width)
{
    if (width == mLastWidth) return;
    setSceneWidth(width);
}

void CPlcScene::setSceneWidth(double width)
{
    mLastWidth = width;
    width      = width * (1 + 0.05 * mScale);
    //预设电流母线占3个像素宽
    double powerRailWidth = 3;
    //单位宽
    double unitWidth     = (width - powerRailWidth) / PLC_MAX_SHOW_ITEM;
    double unitHeight    = unitWidth * 55.0 / 65.0;
    double commentHeight = 0;   //设置默认注释块高
    if (mCommentIsVisible) commentHeight = 35;

    QRectF rect(powerRailWidth, commentHeight, unitWidth, unitHeight);
    QRectF rectComment(powerRailWidth, 0, unitWidth, commentHeight);
    //开始遍历所有元件的显示
    int32_t i = 0, j = 0;
    double x = powerRailWidth, y = 0;
    for (i = 0; i < mListView.size(); i++)
    {
        //获取并遍历单行
        // rowList *list = &mListView[i];
        x = powerRailWidth;   //每一行的第一个，重新设置坐标
        rect.setRect(x, y + commentHeight, unitWidth, unitHeight);
        rectComment.setRect(x, y, unitWidth, commentHeight);
        for (j = 0; j < mListView[i].size(); j++)
        {
            //如果当前元件为空，则创建一个元件
            if (mListView[i][j].pItem == NULL)
            {
                mListView[i][j].pItem =
                    createItem(rect, mListView[i][j].component.type,
                               mListView[i][j].variableNum);
                this->addItem(mListView[i][j].pItem);
                //判断是否有分支
                if (mListView[i][j].component.branch == 1)
                {
                    IPlcBaseItem *branch =
                        createItem(rect, plc::BranchDown, "");
                    mListView[i][j].pItem->setBranch(branch);
                    this->addItem(branch);
                }
            }
            else
            {
                mListView[i][j].pItem->setRect(rect);
            }

            //注释相关
            QString str;
            if (mListView[i][j].pItem->haveComment())
                str = mCommentMap.value(mListView[i][j].variableNum);

            if (mListView[i][j].pComment == NULL)
            {
                mListView[i][j].pComment =
                    new CPlcItemComment(rectComment, plc::Comment, str);
                this->addItem(mListView[i][j].pComment);
            }
            else
            {
                mListView[i][j].pComment->setVariableNum(str);
                mListView[i][j].pComment->setRect(rectComment);
            }
            //设置是否可见
            if (mCommentIsVisible)
                mListView[i][j].pComment->show();
            else
                mListView[i][j].pComment->hide();

            //判断是否包含分支
            IPlcBaseItem *branch = mListView[i][j].pItem->getBranch();
            if (branch != NULL)
            {
                QRectF rect1(rect.right() - 2.5,
                             rect.top() + rect.height() * 35.0 / 55.0, 5,
                             rect.height() + commentHeight);
                branch->setRect(rect1);
            }
            x += unitWidth;
            rect.setRect(x, y + commentHeight, unitWidth, unitHeight);
            rectComment.setRect(x, y, unitWidth, commentHeight);
        }
        y += commentHeight;
        y += unitHeight;
    }

    //把所有元件都遍历完成后，在根据所有元件的总高度，设置电流母线的长度
    double bottom = mListView.at(i - 1).at(0).pItem->getRect().bottom();
    rect.setRect(0, 0, 5, bottom);
    mPowerRail->setRect(rect);
    if (bottom < mMixHeight) bottom = mMixHeight;
    rect.setRect(0, 0, width, bottom);
    setSceneRect(rect);
}

void CPlcScene::resetItemSelectStatus()
{
    IPlcBaseItem *mLastItem = IPlcBaseItem::getOnlySelectItem();
    if (mLastItem != NULL)
    {
        mLastItem->setSelectActive(false);
    }
}
//获取选中的Item图元
IPlcBaseItem *CPlcScene::getSelectItem()
{
    return IPlcBaseItem::getOnlySelectItem();
}

//获取选中的Item图元数据
bool CPlcScene::getSelectItemData(SItemData &itemData)
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (findItem(mListView, item, pos))
    {
        itemData = mListView.at(pos.x).at(pos.y);
        return true;
    }
    return false;
}

//向前插入直线元件
void CPlcScene::insertItem()
{
    /*思路分析：从局部到整体，查找能否在内部实现插入(当前块内存在独立连接线元件)，
     * 如果必须要扩充当前块来实现插入，就合并跳转后的整个逻辑段，插入后整体后移。
     *
     * 1、先判断当前选中元件是否为空，不允许在空元件上插入元件
     * 2、获取当前选中逻辑段的范围（判断当前选中行是否为逻辑块的第一行）
     * 3、判断当前逻辑行是否已经包含跳转元件
     * 4、根据之前判断，重新生成完整的逻辑段（去除链表长度限制）（去除跳转、检查语法）
     * 5、根据完整逻辑段，从选中行处，实现插入，把整个逻辑段向后推一格，空白处填充连接线
     */

    //<1、先判断当前选中元件是否为空，不允许在空元件上插入元件
    IPlcBaseItem *item = getSelectItem();
    if (item == NULL) return;
    if (item->getItemType() == plc::Empty) return;

    //当前元件为右跳转元件时，不允许插入元件
    if (item->getItemType() == plc::RightJump) return;

    //查找当前行位置，根据位置查找完整逻辑段
    SPosition pos;
    if (!findItem(mListView, item, pos)) return;

    // 1、向上找同时向下找，确定当前逻辑块的行范围
    int32_t upRow = pos.x, downRow = pos.x;
    findRelateItemRow(mListView, pos.x, upRow, downRow);

    //判断逻辑块首行的最后一个元件是否为结束元件
    if (!mListView.at(upRow).last().pItem->isEnd()) return;

    // 2、从逻辑块首行开始向下遍历，移除原有逻辑行，生新列表，新列表合并所有移除行的跳转关系
    QList<rowList> fullList;
    int32_t startRow, depth;
    if (!mergeRelateItemRow(mListView, fullList, upRow, startRow, depth))
        return;

    // 3、插入列元件空或者连接线元件，如果列能够后推，则后推一格
    insertColumnOfList(fullList, pos.y);

    // 4、把完整列表再分割成固定行长度的列表
    QList<rowList> partList;
    partItemList(fullList, partList);

    // 5、移除原列表段，补进去新列表段
    for (int32_t i = 0; i < depth; ++i)
    {
        mListView.removeAt(upRow);
    }
    for (int32_t i = 0; i < partList.size(); ++i)
    {
        mListView.insert(upRow + i, partList.at(i));
    }
    // 6、重新整理场景
    setSceneWidth(mLastWidth);
    emit sgnSelectItemChanged();

    //记录回退步
    addStep();
}

//替换元件
void CPlcScene::replaceItem(SItemData &itemData)
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (!findItem(mListView, item, pos)) return;

    //跳转行不允许替换
    if (item->getItemType() == plc::LeftJump ||
        item->getItemType() == plc::RightJump)
        return;

    rowList *rowlist_ = &mListView[pos.x];
    // 判断元件类型是否相同
    if (item->getItemType() == itemData.component.type)
    {
        //相同类型的元件 处理其他参数就可以
        item->setVariableNum(itemData.variableNum);   //设置变量编号
        itemData.pItem    = item;
        itemData.pComment = mListView.at(pos.x).at(pos.y).pComment;
        //替换元素
        mListView[pos.x][pos.y] = itemData;
        // TODO  注释更新
        if (item->haveComment())
        {
            QString str = mCommentMap.value(itemData.variableNum);
            itemData.pComment->setVariableNum(str);
        }

        printLog(tr("相同类型替换"));
        //记录回退步
        addStep();
        setSceneWidth(mLastWidth);   //重新布局显示
        return;
    }

    //根据元件指针是否为空，判断是修改还是替换元件
    if (itemData.pItem != NULL)
    {
        //原有元件做修改
        item->setItemType(itemData.component.type);
        item->setVariableNum(itemData.variableNum);   //设置变量编号
        itemData.pItem    = item;
        itemData.pComment = mListView.at(pos.x).at(pos.y).pComment;
        //替换元素
        mListView[pos.x][pos.y] = itemData;
        //注释更新
        if (item->haveComment())
        {
            QString str = mCommentMap.value(itemData.variableNum);
            itemData.pComment->setVariableNum(str);
        }
        printLog(tr("同大类元件修改编辑"));
        //记录回退步
        addStep();
        setSceneWidth(mLastWidth);   //重新布局显示
        return;
    }

    //生成新元件
    QRectF rect = item->getRect();
    itemData.pItem =
        createItem(rect, itemData.component.type, itemData.variableNum);
    if (itemData.pItem == NULL) return;
    // 1、结束行替换非结束元件不允许
    if (pos.y == (PLC_MAX_SHOW_ITEM - 1))
    {
        //结束行替换结束元件，正常替换
        if (itemData.pItem->isEnd())
        {
            replaceItem(itemData, pos);
            printLog(tr("结束行替换结束元件"));
            //记录回退步
            addStep();
            setSceneWidth(mLastWidth);   //重新布局显示
            return;
        }
        else
        {
            delete itemData.pItem;
            printLog(tr("结束行替换非结束元件不允许"));
            return;
        }
    }

    // 2、非结束行替换结束元件
    if (itemData.pItem->isEnd())
    {
        //如果当前行已经有结束元件存在，不允许替换
        if (rowlist_->last().pItem->isEnd())
        {
            delete itemData.pItem;
            printLog(tr("如果当前行已经有结束元件存在，不允许替换"));
            return;
        }
        //把结束元件添加到最后，之前的元件用连接线补齐
        for (int32_t i = pos.y; i < PLC_MAX_SHOW_ITEM - 1; ++i)
        {
            //空元件
            SItemData itemData1;
            itemData1.pComment = NULL;
            itemData1.pItem    = NULL;
            memset(&itemData1.component, 0, sizeof(itemData1.component));
            itemData1.component.type = plc::Line;
            SPosition pos1           = pos;
            pos1.y                   = i;
            replaceItem(itemData1, pos1);
        }
        //最后再替换结束元件
        SPosition pos2 = pos;
        pos2.y         = PLC_MAX_SHOW_ITEM - 1;
        replaceItem(itemData, pos2);

        printLog(tr("非结束行替换结束元件"));

        //记录回退步
        addStep();
        setSceneWidth(mLastWidth);   //重新布局显示
        return;
    }

    // 3、正常中间元件替换
    replaceItem(itemData, pos);

    //记录回退步
    addStep();
    setSceneWidth(mLastWidth);   //重新布局显示
}

void CPlcScene::replaceItem(SItemData &itemData, const CPlcScene::SPosition pos)
{
    IPlcBaseItem *item = mListView.at(pos.x).at(pos.y).pItem;

    QRectF rect          = item->getRect();
    IPlcBaseItem *branch = item->getBranch();

    if (itemData.pItem == NULL)
        itemData.pItem =
            createItem(rect, itemData.component.type, itemData.variableNum);
    else
        itemData.pItem->setRect(rect);

    removeItem(item);   // 移除当前选中的元件
    removeItem(mListView.at(pos.x).at(pos.y).pComment);
    addItem(itemData.pItem);             //添加新元件
    itemData.pItem->setBranch(branch);   //分支继承
    itemData.component.branch = (branch != NULL);
    //替换元素
    mListView[pos.x][pos.y] = itemData;

    //选中状态切换到下一个元件
    if (pos.y < PLC_MAX_SHOW_ITEM - 1)   //当前行可以右移
    {
        mListView.at(pos.x).at(pos.y + 1).pItem->setSelectActive(true);
    }
    else
    {
        if (pos.x < mListView.size() - 1)   // 还有下一行，跳转到下一行的首位
        {
            mListView.at(pos.x + 1).at(0).pItem->setSelectActive(true);
        }
        else
        {
            // 重新在底部生成一行空白行
            insertRowBackward(pos.x);
            mListView.at(pos.x + 1).at(0).pItem->setSelectActive(true);
        }
    }
    emit sgnSelectItemChanged();
}

//添加分支
void CPlcScene::addBranch()
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (findItem(mListView, item, pos))
    {
        SItemData *itemData = &mListView[pos.x][pos.y];
        //判断是否已经包含分支
        if (itemData->component.branch == 1) return;
        //判断是否为当前行的最后一个元素，最后一个元素不允许添加分支
        if (pos.y == PLC_MAX_SHOW_ITEM - 1) return;
        //判断当前行是否为最后一行
        if (pos.x == mListView.size() - 1)
        {
            // 重新在底部生成一行空白行
            insertRowBackward(pos.x);
        }
        //添加分支
        QRectF rect = item->getRect();
        QRectF rect1(rect.right() - 2.5, (rect.top() + rect.bottom()) / 2, 5,
                     rect.height());
        CPlcItemBranch *item_ = new CPlcItemBranch(rect1, plc::BranchDown, "");
        item->setBranch(item_);
        addItem(item_);
        itemData->component.branch = 1;   // 标识分支状态修改
    }
    //记录回退步
    addStep();
    setSceneWidth(mLastWidth);
}

//移除分支
void CPlcScene::removeBranch()
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (findItem(mListView, item, pos))
    {
        SItemData *itemData = &mListView[pos.x][pos.y];
        //判断是否已经包含分支
        if (itemData->component.branch != 1) return;
        //移除分支
        IPlcBaseItem *branch = item->getBranch();
        removeItem(branch);
        item->setBranch(NULL);
        itemData->component.branch = 0;   // 标识分支状态修改
    }

    //记录回退步
    addStep();
}

//向前插入空白行
void CPlcScene::insertRowForward()
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (findItem(mListView, item, pos))
    {
        insertRowForward(pos.x);
    }
    //记录回退步
    addStep();
}

void CPlcScene::insertRowForward(int32_t row)
{
    //生成空白行列表
    rowList list;
    for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
    {
        //空元件
        SItemData itemData;
        itemData.pItem    = NULL;
        itemData.pComment = NULL;
        memset(&itemData.component, 0, sizeof(itemData.component));
        itemData.component.type = plc::Empty;
        list.append(itemData);
    }
    //顶部插入
    mListView.insert(row, list);
    setSceneWidth(mLastWidth);
}

void CPlcScene::insertRowBackward(int32_t row)
{
    //生成空白行列表
    QRectF rect = mListView.at(row).at(0).pItem->getRect();
    rowList list;
    for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
    {
        //空元件
        SItemData itemData;
        itemData.pItem    = new CPlcItemSpace(rect, plc::Empty, "");
        itemData.pComment = NULL;
        memset(&itemData.component, 0, sizeof(itemData.component));
        itemData.component.type = plc::Empty;
        list.append(itemData);
        addItem(itemData.pItem);
    }
    //行后插入
    if (row == mListView.size() - 1)
        mListView.append(list);
    else
        mListView.insert(row, list);
    setSceneWidth(mLastWidth);
}

//移除行
void CPlcScene::removeCurrentRow()
{
    //判断是否有元件被选中了
    SPosition pos;
    IPlcBaseItem *item = getSelectItem();
    if (findItem(mListView, item, pos))
    {
        removeCurrentRow(pos.x);
        //记录回退步
        addStep();
    }
}

void CPlcScene::removeCurrentRow(int32_t row)
{
    //如果总行数只有一行则不执行
    if (1 == mListView.size()) return;

    //判断行号是否越界
    if (row >= mListView.size()) return;

    //当前行前后都有跳转 把当前行的跳转关系转移
    if (mListView.at(row).first().component.type == plc::RightJump &&
        mListView.at(row).last().component.type == plc::LeftJump)
    {
        IPlcBaseItem *jump1 = mListView.at(row).first().pItem->getFriend();
        IPlcBaseItem *jump2 = mListView.at(row).last().pItem->getFriend();
        jump1->setFriend(jump2);
        jump2->setFriend(jump1);
    }
    //只有左跳转时 移除跳转对应元件
    else if (mListView.at(row).first().component.type == plc::RightJump)
    {
        IPlcBaseItem *jump1 = mListView.at(row).first().pItem->getFriend();
        SPosition pos;
        if (findItem(mListView, jump1, pos))
        {
            removeItem(jump1);
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pItem          = NULL;
            itemData.pComment       = NULL;
            itemData.component.type = plc::Line;
            mListView[pos.x][pos.y] = itemData;
        }
    }
    //只有右跳转时 同上
    else if (mListView.at(row).last().component.type == plc::LeftJump)
    {
        IPlcBaseItem *jump1 = mListView.at(row).last().pItem->getFriend();
        SPosition pos;
        if (findItem(mListView, jump1, pos))
        {
            removeItem(jump1);
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pItem          = NULL;
            itemData.pComment       = NULL;
            itemData.component.type = plc::Line;
            mListView[pos.x][pos.y] = itemData;
        }
    }

    //移除整列

    rowList list = mListView.at(row);
    for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
    {
        //空元件
        SItemData itemData = list.at(i);
        removeItem(itemData.pItem->getBranch());   //移除分支
        removeItem(itemData.pItem);                //移除元件
        removeItem(itemData.pComment);             //注释移除
    }
    mListView.removeAt(row);   //移除单行

    setSceneWidth(mLastWidth);   //重新布局显示
    emit sgnSelectItemChanged();
}

//移除函数
void CPlcScene::removeItem()
{
    if (getSelectItem()->getItemType() == plc::Empty) return;

    if (getSelectItem()->getItemType() == plc::LeftJump ||
        getSelectItem()->getItemType() == plc::RightJump)
        return;

    SItemData itemData;
    memset(&itemData.component, 0, sizeof(SComponent));
    itemData.pItem          = NULL;
    itemData.pComment       = NULL;
    itemData.component.type = plc::Empty;
    replaceItem(itemData);

    //记录回退步
    addStep();
}

//撤销
void CPlcScene::onUndo()
{
    //检查index是否越界
    if (mStepIndex > (mSaveStep.size() - 1)) mStepIndex = mSaveStep.size() - 1;
    if (mStepIndex < 0) mStepIndex = 0;

    //没有撤销选择的余地
    if (mStepIndex < 1) return;

    mStepIndex--;
    removeAll(mListView);
    mListView = mSaveStep.at(mStepIndex);
    loadAll(mListView);

    //界面重新刷新
    setSceneWidth(mLastWidth);
}

//还原
void CPlcScene::onRedo()
{
    //检查index是否越界
    if (mStepIndex > (mSaveStep.size() - 1)) mStepIndex = mSaveStep.size() - 1;
    if (mStepIndex < 0) mStepIndex = 0;

    //没有还原选择的余地
    if (mStepIndex >= (mSaveStep.size() - 1)) return;

    mStepIndex++;
    removeAll(mListView);
    mListView = mSaveStep.at(mStepIndex);
    loadAll(mListView);

    //界面重新刷新
    setSceneWidth(mLastWidth);
}

//放大
void CPlcScene::onZoomIn()
{
    if (mScale < 4) mScale++;
    setSceneWidth(mLastWidth);
}

//缩小
void CPlcScene::onZoomOut()
{
    if (mScale > 0) mScale--;
    setSceneWidth(mLastWidth);
}

//根据变量名查找Item位置
void CPlcScene::findItem(QString variable)
{
    SPosition pos;
    bool index = false;
    //获取当前选中元件的位置
    IPlcBaseItem *item = getSelectItem();
    if (item == NULL)
    {
        pos.x = 0;
        pos.y = 0;
    }
    else
    {
        //当前选中的元件就是查找的元件
        if (item->getVariableNum() == variable)
        {
            //找到当前元件的位置
            findItem(mListView, getSelectItem(), pos);
            index = true;
        }
        else
        {
            pos.x = 0;
            pos.y = 0;
        }
    }

    //先顺序向下查找
    for (int i = 0; i < mListView.size(); ++i)
    {
        for (int j = 0; j < mListView.at(i).size(); ++j)
        {
            if (index)   //初始化搜索的位置
            {
                i = pos.x;
                j = pos.y + 1;   //从当前位置的下一个元件开始找
                index = false;
                if (j >= PLC_MAX_SHOW_ITEM) continue;
            }

            if (mListView.at(i).at(j).variableNum == variable)
            {
                mListView.at(i).at(j).pItem->setSelectActive(true);
                return;
            }
        }
    }
    //最底部没找到的情况下，重新开始从开始查找
    for (int i = 0; i < mListView.size(); ++i)
    {
        for (int j = 0; j < mListView.at(i).size(); ++j)
        {
            if (i == pos.x && j == pos.y)   //查找完毕
            {
                return;
            }
            if (mListView.at(i).at(j).variableNum == variable)
            {
                mListView.at(i).at(j).pItem->setSelectActive(true);
                return;
            }
        }
    }
}

//更新注释列表
void CPlcScene::updateCommentList(/*QMap<QString, QString> &comment*/)
{
    //	Q_UNUSED(comment);
    mCommentMap.insert(QString(""), QString(""));
    mCommentMap.insert(QString("X10"),
                       QString("This is an overloaded function"));
    for (int i = 0; i < 10; ++i)
    {
        mCommentMap.insert(QString("X%1").arg(i),
                           QString::fromLocal8Bit("钥匙开关触点%1").arg(i));
        mCommentMap.insert(QString("Y%1").arg(i),
                           QString::fromLocal8Bit("输出状态灯%1").arg(i));
    }
}

//更新IO值数据
void CPlcScene::updateIoValue()
{
    mIoValueMap.insert("X0", true);
    mIoValueMap.insert("X1", true);
    mIoValueMap.insert("X2", true);
    mIoValueMap.insert("X3", true);
    mIoValueMap.insert("X4", true);
    mIoValueMap.insert("X5", true);
    mIoValueMap.insert("Y0", true);
}

//设置注释是否可见
void CPlcScene::setCommentView(bool value)
{
    mCommentIsVisible = value;
    setSceneWidth(mLastWidth);
}

//编译梯形图
void CPlcScene::compileLad()
{
    //语法检测
    checkIllegal(mListView);
    //保存梯形图
    saveItemList(mListView);
}

//切换到监控梯形图
void CPlcScene::switchToMonitorLad()
{
    //语法检查
    if (checkIllegal(mListView))
    {
        loopTimer.start(150);
        runMonitorLad(mListView);
    }
}

//切换到编辑梯形图
void CPlcScene::switchToEditLad()
{
    //
    for (int32_t i = 0; i < mListView.size(); ++i)
    {
        for (int32_t j = 0; j < PLC_MAX_SHOW_ITEM; ++j)
        {
            mListView.at(i).at(j).pItem->setSwitchOn(false);
            mListView.at(i).at(j).pItem->setLastSwitchOn(false);
            if (mListView.at(i).at(j).component.branch == 1)
            {
                mListView.at(i).at(j).pItem->getBranch()->setSwitchOn(false);
            }
        }
    }
    //母线设置
    mPowerRail->setSwitchOn(false);
    loopTimer.stop();
}

void CPlcScene::removeItem(IPlcBaseItem *item)
{
    if (item == NULL) return;
    QGraphicsScene::removeItem(item);
    delete item;
}

//分割列表，把完整列表分割成固定行长度的列表
void CPlcScene::partItemList(QList<CPlcScene::rowList> &fullList,
                             QList<CPlcScene::rowList> &partList)
{
    //判断当前列表的行长度是否超过设定行的长度
    int32_t fullSize = fullList.size();
    if (fullList.at(0).size() == PLC_MAX_SHOW_ITEM)
    {
        partList = fullList;
        return;
    }
    else if (fullList.at(0).size() < PLC_MAX_SHOW_ITEM)
    {
        SItemData itemData;
        memset(&itemData.component, 0, sizeof(SComponent));
        itemData.pComment = NULL;
        itemData.pItem    = NULL;
        for (int i = 0; i < fullList.size(); ++i)
        {
            int32_t subSize = fullList.at(i).size();
            //判断最后一个元件是否为空，如果为空，就补空元件，否则在最后一个之前，插入连接线
            if (fullList.at(i).last().component.type == plc::Empty)
            {
                itemData.component.type = plc::Empty;
                for (int32_t var = 0; var < PLC_MAX_SHOW_ITEM - subSize; ++var)
                {
                    fullList[i].append(itemData);
                }
            }
            else   //插入连接线
            {
                itemData.component.type = plc::Line;
                for (int32_t var = 0; var < PLC_MAX_SHOW_ITEM - subSize; ++var)
                {
                    fullList[i].insert(subSize - 1, itemData);
                }
            }
        }
        partList = fullList;
        return;
    }

    partList.clear();

    int32_t num = 0;
    //按行遍历
    do
    {
        for (int32_t i = 0; i < fullSize; ++i)
        {
            //提取一行内容
            rowList tempList = fullList.at(i).mid(0, PLC_MAX_SHOW_ITEM - 1);
            partList.append(tempList);
            //移除原列表被提取的内容
            for (int32_t var = 0; var < PLC_MAX_SHOW_ITEM - 1; ++var)
            {
                fullList[i].removeFirst();
            }

            //生成跳转元件
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pComment = NULL;
            //判断下一个元件是否为空，如果为空，则补两个空元件否则补跳转元件
            if (fullList[i].first().component.type == plc::Empty)
            {
                //分割列表最后添加空元件
                itemData.pItem          = NULL;
                itemData.component.type = plc::Empty;
                partList[num * fullSize + i].append(itemData);
                //原列表头部插入空元件
                fullList[i].prepend(itemData);
            }
            else
            {
                CPlcItemJump *jump1 =
                    new CPlcItemJump(QRectF(), plc::LeftJump, "");
                CPlcItemJump *jump2 =
                    new CPlcItemJump(QRectF(), plc::RightJump, "");
                jump1->setFriend(jump2);
                jump2->setFriend(jump1);
                addItem(jump1);
                addItem(jump2);
                //分割列表最后添加左跳转
                itemData.pItem          = jump1;
                itemData.component.type = plc::LeftJump;
                partList[num * fullSize + i].append(itemData);
                //原列表头部插入对应的右跳转
                itemData.pItem          = jump2;
                itemData.component.type = plc::RightJump;
                fullList[i].prepend(itemData);
            }
        }
        ++num;
    } while (fullList.at(0).size() > PLC_MAX_SHOW_ITEM);

    //判断最后一行是否满元件，如果不满，就补齐
    for (int32_t i = 0; i < fullSize; ++i)
    {
        SItemData itemData;
        memset(&itemData.component, 0, sizeof(SComponent));
        itemData.pComment = NULL;
        int32_t subSize   = fullList.at(i).size();
        //判断最后一个元件是否为空，如果为空，就补空元件，否则在最后一个之前，插入连接线
        if (fullList.at(i).last().component.type == plc::Empty)
        {
            itemData.pItem          = NULL;
            itemData.component.type = plc::Empty;
            for (int32_t var = 0; var < PLC_MAX_SHOW_ITEM - subSize; ++var)
            {
                fullList[i].append(itemData);
            }
        }
        else   //插入连接线
        {
            itemData.pItem          = NULL;
            itemData.component.type = plc::Line;
            for (int32_t var = 0; var < PLC_MAX_SHOW_ITEM - subSize; ++var)
            {
                fullList[i].insert(subSize - 1, itemData);
            }
        }
        partList.append(fullList.at(i));
    }

    //去除纯粹的空元件行，当前行所有元件都为空时，去除掉
    for (int32_t i = 0; i < partList.size(); ++i)
    {
        //判断是否为空
        bool result  = true;
        rowList list = partList.at(i);
        for (int32_t j = 0; j < list.size(); ++j)
        {
            if (list.at(j).component.type != plc::Empty)
            {
                result = false;
                break;
            }
        }
        //执行空，去除元件
        if (result)
        {
            for (int32_t k = 0; k < list.size(); ++k)
            {
                removeItem(list.at(k).pItem);
                removeItem(list.at(k).pComment);
            }
            partList.removeAt(i);
            --i;
        }
    }
}

//对齐列表，列表列数不一致时，对齐列表到和最长列一致
void CPlcScene::alignItemList(QList<CPlcScene::rowList> &list)
{
    //查找到最长列
    int32_t max = 0, iMax = 0;
    for (int32_t i = 0; i < list.size(); ++i)
    {
        if (list.at(i).size() > max)
        {
            iMax = i;
            max  = list.at(i).size();
        }
    }
    for (int32_t i = 0; i < list.size(); ++i)
    {
        //最后一个元件不为空时，插入直线元件，否则插入空元件
        SItemData itemData;
        memset(&itemData.component, 0, sizeof(SComponent));
        itemData.pItem          = NULL;
        itemData.pComment       = NULL;
        itemData.component.type = plc::Empty;

        if (list.at(i).last().pItem->getItemType() != plc::Empty)
            itemData.component.type = plc::Line;

        for (int32_t j = list.at(i).size(); j < iMax; ++j)
        {
            list[i].append(itemData);
        }
    }
}

//根据指导Item行查找关联行范围
void CPlcScene::findRelateItemRow(const QList<CPlcScene::rowList> &list,
                                  int32_t selRow,
                                  int32_t &upRow,
                                  int32_t &downRow)
{
    upRow   = selRow;
    downRow = selRow;
    //向上查找
    while (upRow > 0)
    {
        bool value = false;
        for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
        {
            //判断上一行所有元件是否有分支
            if (list.at(upRow - 1).at(i).component.branch == 1)
            {
                value = true;
                upRow--;
                break;
            }
        }
        if (!value) break;
    }
    //向下查找-
    while (downRow < list.size())
    {
        bool value = false;
        for (int32_t i = 0; i < PLC_MAX_SHOW_ITEM; i++)
        {
            //判断上一行所有元件是否有分支
            if (list.at(downRow).at(i).component.branch == 1)
            {
                value = true;
                downRow++;
                break;
            }
        }
        if (!value) break;
    }
}

//合并指定行范围的梯形图列表，把所有跳转关系合并
bool CPlcScene::mergeRelateItemRow(const QList<CPlcScene::rowList> &list,
                                   QList<CPlcScene::rowList> &fullList,
                                   int32_t selRow,
                                   int &startRow,
                                   int &depth)
{
    //初始化参数

    int32_t nexStart, nexDown;   //上下行
    fullList.clear();
    //查找选中行上下关联关系
    findRelateItemRow(list, selRow, startRow, nexDown);
    nexStart = startRow;

    //把第一段数据装进列表
    int32_t rowSize = nexDown - nexStart + 1;
    for (int32_t i = 0; i < rowSize; i++)
    {
        fullList.append(list.at(nexStart + i));
    }
    int32_t lastSize = rowSize;

    //开始循环向下遍历
    while (fullList.first().last().component.type ==
           plc::LeftJump)   //首行末尾是否为跳转
    {
        //查找跳转关系
        IPlcBaseItem *item = fullList.first().last().pItem->getFriend();
        SPosition pos;
        if (!findItem(list, item, pos)) return false;
        //查找选中行上下关联关系
        findRelateItemRow(list, pos.x, nexStart, nexDown);
        rowSize = nexDown - nexStart + 1;

        //去除最后一列的跳转过渡元件
        for (int32_t i = 0; i < fullList.size(); ++i)
        {
            removeItem(fullList.at(i).last().pItem);
            fullList[i].removeLast();
        }

        //继续向后添加元件

        for (int32_t i = 0; i < ((rowSize > lastSize) ? rowSize : lastSize);
             ++i)
        {
            //当前列深度大于新列表总深度，新列表深度添加空元件
            if (i >= lastSize)
            {
                rowList subList;
                SItemData itemData;
                memset(&itemData.component, 0, sizeof(SComponent));
                itemData.pItem          = NULL;
                itemData.pComment       = NULL;
                itemData.component.type = plc::Empty;
                for (int32_t j = 0;
                     j < fullList.first().size() - PLC_MAX_SHOW_ITEM + 1; ++j)
                {
                    subList.append(itemData);
                }
                fullList.append(subList);
            }
            //当前列深度小于新列表总深度，新列表后添加空元件
            if (i >= rowSize)
            {
                rowList subList;
                SItemData itemData;
                memset(&itemData.component, 0, sizeof(SComponent));
                itemData.pItem          = NULL;
                itemData.pComment       = NULL;
                itemData.component.type = plc::Empty;
                for (int32_t j = 0; j < PLC_MAX_SHOW_ITEM - 1; ++j)
                {
                    subList.append(itemData);
                }
                fullList[i].append(subList);
                continue;
            }
            //移除首列
            rowList subList = list.at(nexStart + i);
            removeItem(subList.first().pItem);
            subList.removeFirst();
            //正常情况，移除原列表，追加新列表
            fullList[i].append(subList);
        }
        //		//遍历深度叠加
        //		depth += ((rowSize>lastSize)?rowSize:lastSize);
    }
    depth = nexDown - startRow + 1;
    return true;
}

//在指定列插入空元件或者连接线元件，如果列能够后推，则后推一格
void CPlcScene::insertColumnOfList(QList<CPlcScene::rowList> &list,
                                   int32_t column)
{
    //判断新生成的完整链表，能否去除倒数第二个元件（独立连接线或者空行）,且没有分支
    bool allowRemove = true;
    for (int32_t i = 0; i < list.size(); ++i)
    {
        rowList subList = list.at(i);
        if (subList.at(subList.size() - 2).component.type != plc::Line &&
            subList.at(subList.size() - 2).component.type != plc::Empty)
        {
            allowRemove = false;
            break;
        }
        //判断是否含有分支
        if (subList.at(subList.size() - 2).component.branch == 1)
        {
            allowRemove = false;
            break;
        }
    }

    //先插入列
    for (int32_t i = 0; i < list.size(); ++i)
    {
        //判断当前元件是否为空，空元件插入空，其他插入连接线
        if (list.at(i).at(column).component.type == plc::Empty)
        {
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pItem          = NULL;
            itemData.pComment       = NULL;
            itemData.component.type = plc::Empty;
            list[i].insert(column, itemData);
        }
        else   //插入连接线
        {
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pItem          = NULL;
            itemData.pComment       = NULL;
            itemData.component.type = plc::Line;
            list[i].insert(column, itemData);   //插入列表
        }
    }

    //再执行去除倒数第二个元件操作
    if (allowRemove)
    {
        int32_t index = list.at(0).size() - 2;
        for (int32_t i = 0; i < list.size(); ++i)
        {
            removeItem(list[i].at(index).pItem);   //从场景移除
            list[i].removeAt(index);               //从列表移除
        }
    }
}

//输出日志
void CPlcScene::printLog(QString str)
{
    qDebug() << str;
}

//从场景中移除所有元件
void CPlcScene::removeAll(QList<CPlcScene::rowList> &list)
{
    //移除所有元件、分支、注释
    for (int32_t i = 0; i < list.size(); ++i)
    {
        for (int32_t j = 0; j < PLC_MAX_SHOW_ITEM; ++j)
        {
            removeItem(list.at(i).at(j).pItem->getBranch());
            removeItem(list.at(i).at(j).pComment);
            removeItem(list.at(i).at(j).pItem);
        }
    }
}

//加载所有元件到场景
void CPlcScene::loadAll(QList<CPlcScene::rowList> &list)
{
    //所有Item置为空，重新调整界面时再重新生成
    for (int32_t i = 0; i < list.size(); ++i)
    {
        for (int32_t j = 0; j < PLC_MAX_SHOW_ITEM; ++j)
        {
            list[i][j].pComment = NULL;
            list[i][j].pItem    = NULL;
        }
    }
}

//添加一个记录步骤
void CPlcScene::addStep()
{
    //检查index是否越界
    if (mStepIndex >= mSaveStep.size()) mStepIndex = mSaveStep.size() - 1;
    if (mStepIndex < 0) mStepIndex = 0;

    //移除放弃的分支
    for (int32_t i = 0; i < mSaveStep.size() - mStepIndex - 1; ++i)
    {
        mSaveStep.removeLast();
    }

    //添加分支
    mSaveStep.append(mListView);

    mStepIndex++;

    //判断是否越界
    if (mSaveStep.size() > PLC_MAX_SAVE_STEP)
    {
        mSaveStep.removeFirst();
        mStepIndex--;
    }
}

//保存梯形图
void CPlcScene::saveItemList(QList<CPlcScene::rowList> &list, QString path)
{
    //写梯形图列表为文本，列表一行写一行文本
    //元件内参数用|分割，元件之间用&符号分割
    if (path == "")
    {
        path = QDir::currentPath();
        path.append("/temp.lad");
    }
    QFile file(path);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        printLog(tr("创建或打开文件失败"));
        return;   //创建文件失败
    }
    QTextStream data(&file);
    data.setCodec("UTF-8");
    data.setGenerateByteOrderMark(true);

    for (int i = 0; i < list.size(); ++i)
    {
        rowList row = list.at(i);
        for (int j = 0; j < row.size(); ++j)
        {
            SItemData itemData = row.at(j);

            data << QString::number(itemData.component.type) << "|";   //类型
            data << QString::number(itemData.component.name) << "|";   //元件名
            data << QString::number(itemData.component.code)
                 << "|";   //元件代号
            data << QString::number(itemData.component.param) << "|";   //参数
            data << QString::number(itemData.component.branch);         //分支
            if (j == (row.size() - 1))   //是否为最后一个元件
            {
                if (i != (list.size() - 1))   //非最后一排，添加换行
                    data << "\n";
            }
            else
                data << "&";
        }
    }
    file.close();
    printLog(tr("保存梯形图成功"));
}

//读取保存的梯形图
bool CPlcScene::readItemList(QList<CPlcScene::rowList> &list, QString path)
{
    //写梯形图列表为文本，列表一行写一行文本
    //元件内参数用|分割，元件之间用&符号分割
    if (path == "")
    {
        path = QDir::currentPath();
        path.append("/temp.lad");
    }
    QFile file(path);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        printLog(tr("创建或打开文件失败"));
        return false;   //创建文件失败
    }
    QTextStream data(&file);
    data.setCodec("UTF-8");
    data.setGenerateByteOrderMark(true);

    list.clear();
    while (!data.atEnd())
    {
        rowList row;
        QString line         = data.readLine();   //单行读取
        QStringList itemList = line.split("&");   //分割Item
        for (int i = 0; i < itemList.size(); ++i)
        {
            QString strItem       = itemList.at(i);
            QStringList component = strItem.split("|");
            if (component.size() != 5)
            {
                printLog(tr("文件格式错误，读取失败"));
                file.close();
                return false;
            }
            SItemData itemData;
            memset(&itemData.component, 0, sizeof(SComponent));
            itemData.pItem    = NULL;
            itemData.pComment = NULL;

            itemData.component.type =
                static_cast<EComponentType>(component.at(0).toUInt());
            itemData.component.name =
                static_cast<EComponentName>(component.at(1).toUInt());
            itemData.component.code  = component.at(2).toUShort();
            itemData.component.param = component.at(3).toUInt();
            itemData.component.branch =
                static_cast<int8_t>(component.at(4).toUInt());
            itemData.variableNum = generateVariableName(itemData.component);
            row.append(itemData);
        }
        list.append(row);
    }
    file.close();
    printLog(tr("读取梯形图成功"));
    return true;
}

//通过变量名类型，生成变量名
QString CPlcScene::generateVariableName(SComponent &component)
{
    switch (component.name)
    {
    case plc::Input_X:
        return QString("X%1").arg(component.code);
    case plc::Input_SX:
        return QString("SX%1").arg(component.code);
    case plc::Output_Y:
        return QString("Y%1").arg(component.code);
    case plc::Output_SY:
        return QString("SY%1").arg(component.code);
    case plc::Coil_MC:
        return QString("MC%1").arg(component.code);
    case plc::Coil_SMC:
        return QString("SMC%1").arg(component.code);
    case plc::Register_UI:
        return QString("UI%1.%2").arg(component.code).arg(component.param);
    case plc::Counter_CTU:
        return QString("C%1").arg(component.code);
    case plc::Timer_T:
        return QString("T%1").arg(component.code);
    case plc::Timer_ST:
        return QString("ST%1").arg(component.code);
    default:
        return "";
    }
}

//检测梯形图语法合法性
bool CPlcScene::checkIllegal(QList<CPlcScene::rowList> &list)
{
    /*
     * 1、非线首元件，检测前面元件是否为空，如果前面为空，检测前面上一行元件是否为分支，有分支且为空，向上找分支
     * 2、非线尾元件，检测后面元件是否为空，如果后面为空，检测上一行元件是否为分支，有分支且为空，继续向上找分支，直到找到非空元件分支。
     * 3、中间元件，检测前后。
     */
    for (int i = 0; i < list.size(); ++i)
    {
        rowList row = list.at(i);
        for (int j = 0; j < row.size(); ++j)
        {
            //当前元件为空元件时,直接跳过
            if (row.at(j).component.type == plc::Empty) continue;
            //检测当前元件的前一个元件
            if (j > 0)
            {
                //前一个元件为空
                if (row.at(j - 1).component.type == plc::Empty)
                {
                    int32_t rowNum = i - 1;
                    while (true)
                    {
                        if (rowNum < 0)
                        {
                            printLog(tr("前元件为空且没有跳转"));
                            row.at(j).pItem->setSelectActive(true);
                            return false;
                        }
                        if (list[rowNum][j - 1].component.branch == 1)
                        {
                            //元件为空，继续向上找
                            if (list[rowNum][j - 1].component.type !=
                                plc::Empty)
                                break;
                            rowNum--;
                        }
                        else
                        {
                            printLog(tr("前元件为空且没有跳转"));
                            row.at(j).pItem->setSelectActive(true);
                            return false;
                        }
                    }
                }
            }

            //检测当前元件的后一个元件
            if (j < (row.size() - 1))
            {
                //前一个元件为空
                if (row.at(j + 1).component.type == plc::Empty)
                {
                    int32_t rowNum = i - 1;
                    while (true)
                    {
                        if (rowNum < 0)
                        {
                            printLog(tr("后元件为空且没有跳转"));
                            row.at(j).pItem->setSelectActive(true);
                            return false;
                        }
                        if (list[rowNum][j].component.branch == 1)
                        {
                            //元件为空，继续向上找
                            if (list[rowNum][j].component.type != plc::Empty)
                                break;
                            rowNum--;
                        }
                        else
                        {
                            printLog(tr("后元件为空且没有跳转"));
                            row.at(j).pItem->setSelectActive(true);
                            return false;
                        }
                    }
                }
            }
        }
    }

    /*
     * 4、检测行最后一个元件是否为尾元件
     */

    for (int i = 0; i < list.size(); ++i)
    {
        if (list.at(i).last().component.type != plc::Empty &&
            !list.at(i).last().pItem->isEnd())
        {
            printLog(tr("行尾元件不是结束元件"));
            list.at(i).last().pItem->setSelectActive(true);
            return false;
        }
    }

    /*
     * 5、检查跳转行是否小于逻辑关联行（上一跳转通过分支关联到下一跳转行）
     */
    for (int i = 0; i < list.size(); ++i)
    {
        if (list.at(i).first().component.type == plc::RightJump)
        {
            if (i > 0)
            {
                //遍历上一行是否有分支
                for (int j = 0; j < list.at(i - 1).size(); ++j)
                {
                    if (list.at(i - 1).at(j).component.branch == 1)
                    {
                        printLog(tr("分支连接到跳转行"));
                        list.at(i - 1).at(j).pItem->setSelectActive(true);
                        return false;
                    }
                }
            }
        }
    }

    /*
     * 6、双线圈检查
     */
    rowList coilList;
    //遍历所有输出线圈
    for (int i = 0; i < list.size(); ++i)
    {
        rowList row = list.at(i);
        for (int j = 0; j < row.size(); ++j)
        {
            if (row.at(j).component.type == plc::Coil)
                coilList.append(row.at(j));
        }
    }

    //查找是否有双线圈
    for (int i = 0; i < coilList.size(); ++i)
    {
        for (int j = i + 1; j < coilList.size(); ++j)
        {
            //判断是否有相同变量名，确定是否存在双线圈
            if (coilList.at(i).variableNum == coilList.at(j).variableNum)
            {
                printLog(tr("双线圈输出"));
                coilList.at(j).pItem->setSelectActive(true);
                return false;
            }
        }
    }

    /*
     * 7、梯形图整理
     * 思路：判断当前元件的前一个元件是否为连接线
     *		判断连接线元件是否包含分支，或者上一行的元件是否包含分支
     *		当前元件和前一个无关联的连接线元件互换，分支关系不变
     */

    //按行遍历
    for (int i = 0; i < list.size(); ++i)
    {
        // 1、从逻辑块首行开始向下遍历，生新列表，新列表合并所有移除行的跳转关系
        int32_t upRow = i;
        QList<rowList> fullList;
        int32_t startRow, depth;
        if (!mergeRelateItemRow(list, fullList, upRow, startRow, depth))
        {
            printLog(tr("跳转行错误"));
            return false;
        }
        // 2、顺序检查是否可以前移
        inOrderItemList(fullList);

        // 4、把整理完成的列表再重新分割
        QList<rowList> partList;
        partItemList(fullList, partList);

        // 5、移除原列表段，补进去新列表段
        for (int32_t i = 0; i < depth; ++i)
        {
            list.removeAt(upRow);
        }
        for (int32_t i = 0; i < partList.size(); ++i)
        {
            list.insert(upRow + i, partList.at(i));
        }

        // 6、数据行下移
        i += depth;
    }

    printLog(tr("整理梯形图成功"));

    // 8、重新整理场景
    setSceneWidth(mLastWidth);
    emit sgnSelectItemChanged();

    //记录回退步
    addStep();
    return true;
}

//整理梯形图,使之有序
void CPlcScene::inOrderItemList(QList<CPlcScene::rowList> &list)
{
    /*
     * 思路：判断当前元件的前一个元件是否为连接线
     *		判断连接线元件是否包含分支，或者上一行的元件是否包含分支
     *		当前元件和前一个无关联的连接线元件互换，分支关系不变
     */

    //单元件向前移动
    for (int32_t i = 0; i < list.size(); ++i)
    {
        rowList row = list.at(i);
        for (int32_t j = 1; j < row.size(); ++j)
        {
            int32_t lastJ = j;
            //判断当前元件的前一个元件是否为连接线
            while (j > 0)
            {
                row = list.at(i);
                //当前元件非连接线非空，且前面元件为连接线
                if (row.at(j - 1).component.type == plc::Line &&
                    row.at(j).component.type != plc::Line &&
                    row.at(j).component.type != plc::Empty)
                {
                    //判断连接线元件是否包含分支，或者上一行的元件是否包含分支
                    if (row.at(j - 1).component.branch == 1)
                        break;
                    else if (i > 0 &&
                             list.at(i - 1).at(j - 1).component.branch == 1)
                        break;
                    else
                    {
                        //元件和前面的连接线交换，分支关系不变
                        SItemData last    = row.at(j - 1);
                        SItemData current = row.at(j);
                        //当前元件存在分支时,分支交换
                        if (current.component.branch == 1)
                        {
                            last.pItem->setBranch(current.pItem->getBranch());
                            last.component.branch = 1;
                            current.pItem->setBranch(NULL);
                            current.component.branch = 0;
                        }
                        //元件交换
                        list[i][j - 1] = current;
                        list[i][j]     = last;
                        j--;
                    }
                }
                else
                    break;
            }
            j = lastJ;
        }
    }

    //移除整列的空格或者连接线
    for (int32_t j = 0; j < list.first().size(); ++j)
    {
        //先判断这一列是否属于可被删除的列
        bool result = true;
        for (int32_t i = 0; i < list.size(); ++i)
        {
            if (list[i][j].component.type != plc::Empty &&
                list[i][j].component.type != plc::Line)
            {
                result = false;
                break;
            }
        }
        //执行列删除操作
        if (result)
        {
            for (int32_t i = 0; i < list.size(); ++i)
            {
                if (j != 0)   //分支前移一行
                {
                    //当前元件存在分支
                    if (list[i][j].component.branch == 1)
                    {
                        list[i][j - 1].pItem->setBranch(
                            list[i][j].pItem->getBranch());
                        list[i][j - 1].component.branch = 1;
                    }
                }
                //移除元件
                removeItem(list[i][j].pItem);
                removeItem(list[i][j].pComment);
                list[i].removeAt(j);
            }
            j--;
        }
    }
    //判断当前行是否有输出元件，如果有，移到和最后一列对齐
    for (int32_t i = 0; i < list.size(); ++i)
    {
        if (list.at(i).last().pItem->isEnd()) continue;
        for (int32_t j = 0; j < list.at(i).size(); ++j)
        {
            if (list.at(i).at(j).pItem->isEnd())
            {
                list[i].swap(j, list.at(i).size() - 1);
                continue;
            }
        }
    }
}

//运行监控梯形图
void CPlcScene::runMonitorLad(QList<rowList> &list)
{
    // 1、重新生成梯形图元件值列表
    QList<QList<SItemValue> > valueList = crateValueList(list);

    // 2、获取变量值
    updateIoValue();

    // 3、获取跳转最大关联行
    int32_t maxNum = getMaxJumpNum(list);

    // 4、获取所有元件的接通状态
    for (int32_t i = 0; i <= maxNum; ++i)
    {
        for (int32_t j = 0; j < list.first().size(); ++j)
        {
            for (int32_t k = 0; k < list.size(); ++k)
            {
                SPosition pos;
                pos.x = k;
                pos.y = j;
                operateOneItemLogic(list, valueList, pos);
            }
        }
    }

    // 5、把元件值应用在梯形图上

    for (int32_t i = 0; i < list.size(); ++i)
    {
        for (int32_t j = 0; j < list.at(i).size(); ++j)
        {
            list.at(i).at(j).pItem->setSwitchOn(valueList.at(i).at(j).current);
            list.at(i).at(j).pItem->setLastSwitchOn(valueList.at(i).at(j).last);
        }
    }

    // 6、设置母线为接通状态
    mPowerRail->setSwitchOn(true);
}

//创建梯形图原理值列表
QList<QList<CPlcScene::SItemValue> >
CPlcScene::crateValueList(QList<CPlcScene::rowList> &list)
{
    QList<QList<CPlcScene::SItemValue> > valueList;
    SItemValue itemValue{false, false};
    for (int32_t i = 0; i < list.size(); ++i)
    {
        QList<SItemValue> tempList;
        for (int32_t j = 0; j < list.at(i).size(); ++j)
        {
            tempList.append(itemValue);
            //			qDebug() << tempList.at(j).last <<
            // tempList.at(j).current;
        }
        valueList.append(tempList);
    }
    return valueList;
}

//处理单个元件的逻辑
void CPlcScene::operateOneItemLogic(
    QList<CPlcScene::rowList> &list,
    QList<QList<CPlcScene::SItemValue> > &valueList,
    CPlcScene::SPosition pos)
{
    //分三类情况处理，1、前值等于后值， 2、根据前值查找当前值
    // 3、后值一直未0（空格）
    /*
     * 分三类情况处理思路：
     * 1、前值等于后值
     * 2、根据前值查找当前值  触点、线圈
     * 3、后值一直为0
     * 4、根据前值跳转 leftJump
     */

    //挨着母线的元件，除跳转以外，前变量值都应该为true
    if (pos.y == 0)
    {
        if (list.at(pos.x).at(pos.y).component.type != plc::RightJump)
        {
            valueList[pos.x][pos.y].last = true;
        }
    }

    //根据元件前变量，判断当前值，以及后变量
    switch (list.at(pos.x).at(pos.y).component.type)
    {
    case plc::Line:   //前后值相等
    case plc::RightJump:
    case plc::TON:
    case plc::TOF:
    case plc::TONR:
    case plc::CTU:
    case plc::RST:
        valueList[pos.x][pos.y].current = valueList[pos.x][pos.y].last;
        //处理后一个元件
        if (valueList[pos.x][pos.y].current && pos.y < list[pos.x].size() - 1)
            valueList[pos.x][pos.y + 1].last = true;
        break;

    case plc::LeftJump:   //查找跳转行
        valueList[pos.x][pos.y].current = valueList[pos.x][pos.y].last;
        //跳转元件值为true,则继续查找跳转关联元件
        if (valueList[pos.x][pos.y].current)
        {
            SPosition tempPos;
            IPlcBaseItem *item = list[pos.x][pos.y].pItem->getFriend();
            if (findItem(list, item, tempPos))
            {
                valueList[tempPos.x][tempPos.y].last = true;
            }
        }
        break;

    case plc::Empty:
        valueList[pos.x][pos.y].current = false;
        //不操作后一个元件
        break;

    case plc::Pulse:
    case plc::Falling:
        valueList[pos.x][pos.y].current = false;
        //下一个元件起始值置为false
        if (pos.y < list[pos.x].size() - 1)
            valueList[pos.x][pos.y + 1].last = false;
        break;

    case plc::OpenContact:
        valueList[pos.x][pos.y].current =
            valueList[pos.x][pos.y].last &&
            mIoValueMap.value(list[pos.x][pos.y].variableNum, false);
        //处理后一个元件
        if (valueList[pos.x][pos.y].current && pos.y < list[pos.x].size() - 1)
            valueList[pos.x][pos.y + 1].last = true;
        break;

    case plc::CloseContact:
        valueList[pos.x][pos.y].current =
            valueList[pos.x][pos.y].last &&
            (!mIoValueMap.value(list[pos.x][pos.y].variableNum, false));
        //处理后一个元件
        if (valueList[pos.x][pos.y].current && pos.y < list[pos.x].size() - 1)
            valueList[pos.x][pos.y + 1].last = true;
        break;

    default:
        //其他元件 读取元件触点的值
        valueList[pos.x][pos.y].current =
            mIoValueMap.value(list[pos.x][pos.y].variableNum, false);
        break;
    }

    ////////判断分支情况///////////////
    //当前值为false不判断分支情况，当前为尾元件，不判断分支情况
    if (!valueList[pos.x][pos.y].current) return;
    if (pos.y >= list[pos.x].size() - 1) return;

    //判断当前元件关联分支的上、下限。处理上、下限下一个元件
    int32_t upRow = pos.x, downRow = pos.x;
    //向上找
    while (upRow > 0 && list[upRow - 1][pos.y].component.branch == 1)
    {
        upRow--;
    }
    //向下找
    while (downRow < list.size() && list[downRow][pos.y].component.branch == 1)
    {
        downRow++;
    }
    //按条件处理分支和分支后一个元件的状态
    for (int32_t i = upRow; i <= downRow; ++i)
    {
        //列元件下一个不为空
        if (list[i][pos.y + 1].component.type != plc::Empty)
        {
            //处理分支和分支后一行的起始值状态
            if (i < pos.x)   //上部分
            {
                for (int32_t j = i; j < pos.x; ++j)
                {
                    list.at(j).at(pos.y).pItem->getBranch()->setSwitchOn(true);
                }
                valueList[i][pos.y + 1].last = true;
            }
            else if (i > pos.x)   //下部分
            {
                for (int32_t j = pos.x; j < i; ++j)
                {
                    list.at(j).at(pos.y).pItem->getBranch()->setSwitchOn(true);
                }
                valueList[i][pos.y + 1].last = true;
            }
        }
    }
}

//获取跳转关系最大层级数:
int32_t CPlcScene::getMaxJumpNum(QList<CPlcScene::rowList> &list)
{
    int32_t maxNum = 0;
    for (int32_t i = list.size() - 1; i >= 0; --i)
    {
        SPosition tempPos;
        int32_t num = 0;
        //当每行的第一个元件，判断是否为承接跳转元件，如果是，向上查找最大跳转次数
        while (list.at(i).first().component.type == plc::RightJump)
        {
            IPlcBaseItem *item = list.at(i).first().pItem->getFriend();

            if (!findItem(list, item, tempPos)) return 0;

            num++;
            i = tempPos.x;
        }
        if (num > maxNum) maxNum = num;
    }

    return maxNum;
}

void CPlcScene::onLeftSelectItem()
{
    SPosition pos;
    if (findItem(mListView, getSelectItem(), pos))
    {
        if (pos.y == 0) return;
        mListView[pos.x][pos.y - 1].pItem->setSelectActive(true);
        emit sgnSelectItemChanged();
    }
}

void CPlcScene::onRightSelectItem()
{
    SPosition pos;
    if (findItem(mListView, getSelectItem(), pos))
    {
        if (pos.y == (PLC_MAX_SHOW_ITEM - 1)) return;
        mListView[pos.x][pos.y + 1].pItem->setSelectActive(true);
        emit sgnSelectItemChanged();
    }
}

void CPlcScene::onUpSelectItem()
{
    SPosition pos;
    if (findItem(mListView, getSelectItem(), pos))
    {
        if (pos.x == 0) return;
        mListView[pos.x - 1][pos.y].pItem->setSelectActive(true);
        emit sgnSelectItemChanged();
    }
}

void CPlcScene::onDownSelectItem()
{
    SPosition pos;
    if (findItem(mListView, getSelectItem(), pos))
    {
        if (pos.x == (mListView.size() - 1)) return;
        mListView[pos.x + 1][pos.y].pItem->setSelectActive(true);
        emit sgnSelectItemChanged();
    }
}

void CPlcScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QTransform transform;
    QGraphicsItem *item = this->itemAt(event->scenePos(), transform);
    if (item != NULL)
    {
        //操作当前鼠标点击的图元
        IPlcBaseItem *item_ = (IPlcBaseItem *)item;
        if (item_->getSelectActiveEnb())
        {
            //复位上一次选中的图元
            resetItemSelectStatus();
            item_->setSelectActive(true);
            emit sgnSelectItemChanged();
        }
    }
    //鼠标事件重新传递出去
    QGraphicsScene::mouseReleaseEvent(event);
}

//重写键盘按下事件
void CPlcScene::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    case Qt::Key_Up:
        onUpSelectItem();
        break;
    case Qt::Key_Down:
        onDownSelectItem();
        break;
    case Qt::Key_Left:
        onLeftSelectItem();
        break;
    case Qt::Key_Right:
        onRightSelectItem();
        break;
        //	case Qt::Key_0:
        //		cout << mPowerRail->getRect().width();
        //		cout << this->sceneRect().width();
        //		cout << this->sceneRect().height();
        //		break;
    default:
        QGraphicsScene::keyPressEvent(event);
    }
}

//遍历查找
bool CPlcScene::findItem(const QList<CPlcScene::rowList> &list,
                         IPlcBaseItem *item,
                         CPlcScene::SPosition &pos)
{
    if (item == NULL) return false;

    for (int32_t i = 0; i < list.size(); i++)
    {
        rowList subList = list.at(i);
        for (int32_t j = 0; j < subList.size(); j++)
        {
            if (subList.at(j).pItem == item)
            {
                pos.x = i;
                pos.y = j;
                return true;
            }
        }
    }
    return false;
}

//判断两个元件类型 大类是否相同
bool CPlcScene::typeCmp(uint8_t type1, uint8_t type2)
{
    if (type1 == type2) return true;

    if (type1 < 10) return false;

    if ((type1 >= 10 && type1 < 20) && (type2 >= 10 && type2 < 20)) return true;
    if ((type1 >= 40 && type1 < 50) && (type2 >= 40 && type2 < 50)) return true;
    if ((type1 >= 100 && type1 < 110) && (type2 >= 100 && type2 < 110))
        return true;

    return false;
}

//创建元件
IPlcBaseItem *CPlcScene::createItem(QRectF rect, uint8_t type, QString variable)
{
    IPlcBaseItem *item = NULL;
    switch (type)
    {
    case plc::Empty:
        item = new CPlcItemSpace(rect, type, variable);
        break;
    case plc::Line:
        item = new CPlcItemLinkLine(rect, type, variable);
        break;
    case plc::OpenContact:
        item = new CPlcItemContact(rect, type, variable);
        break;
    case plc::CloseContact:
        item = new CPlcItemContact(rect, type, variable);
        break;
    case plc::Pulse:
        item = new CPlcItemContact(rect, type, variable);
        break;
    case plc::Falling:
        item = new CPlcItemContact(rect, type, variable);
        break;
    case plc::Coil:
        item = new CPlcItemCoil(rect, type, variable);
        break;
    case plc::Set:
        item = new CPlcItemCoil(rect, type, variable);
        break;
    case plc::Reset:
        item = new CPlcItemCoil(rect, type, variable);
        break;
    case plc::TON:
    case plc::TOF:
    case plc::TONR:
    case plc::CTU:
    case plc::RST:
        item = new CPlcItemFunctionBlock(rect, type, variable);
        break;
    case plc::LeftJump:
        item = new CPlcItemJump(rect, type, variable);
        break;
    case plc::RightJump:
        item = new CPlcItemJump(rect, type, variable);
        break;
    case plc::BranchDown:
        item = new CPlcItemBranch(rect, type, variable);
        break;
    }
    return item;
}
